using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Ricciolo.StylesExplorer.Model
{
    public sealed class CustomThreadPool : IDisposable
    {
        private Semaphore _workWaiting;
        private Queue<WaitQueueItem> _queue;
        private List<Thread> _threads;

        public CustomThreadPool(int numThreads)
        {
            if (numThreads <= 0)
                throw new ArgumentOutOfRangeException("numThreads");

            _threads = new List<Thread>(numThreads);
            _queue = new Queue<WaitQueueItem>();
            _workWaiting = new Semaphore(0, int.MaxValue);

            for (int i = 0; i < numThreads; i++)
            {
                Thread t = new Thread(Run);
                t.IsBackground = true;
                _threads.Add(t);
                t.Start();
            }
        }

        ~CustomThreadPool()
        {
            Dispose(false);
        }

        private static CustomThreadPool _dependenciesThreadPool = new CustomThreadPool(1);

        public static CustomThreadPool Default
        {
            get { return _dependenciesThreadPool; }
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (_threads != null)
            {
                _threads.ForEach(delegate(Thread t) { t.Abort(); });
                _threads.ForEach(delegate(Thread t) { t.Join(500); });
                _threads = null;
            }
        }

        public void QueueUserWorkItem(WaitCallback callback, object state)
        {
            if (_threads == null)
                throw new ObjectDisposedException(GetType().Name);
            if (callback == null) throw new ArgumentNullException("callback");

            WaitQueueItem item = new WaitQueueItem();
            item.Callback = callback;
            item.State = state;
            item.Context = ExecutionContext.Capture();

            lock (_queue) _queue.Enqueue(item);
            _workWaiting.Release();
        }

        private void Run()
        {
            try
            {
                while (true)
                {
                    _workWaiting.WaitOne();
                    WaitQueueItem item;
                    lock (_queue) item = _queue.Dequeue();
                    ExecutionContext.Run(item.Context,
                        new ContextCallback(item.Callback), item.State);
                }
            }
            catch (ThreadInterruptedException) { }
        }

        private class WaitQueueItem
        {
            public WaitCallback Callback;
            public object State;
            public ExecutionContext Context;
        }
    }


}
